home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / pascal / iolog.com / IOLOG.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1988-12-20  |  16.7 KB  |  538 lines

  1. {
  2.  IOLOG - a TSR that logs all DOS disk I/O operations to a file for later
  3.  study.
  4.  
  5.  To install, type
  6.    IOLOG [LogFileName]
  7.  
  8.  The log file defaults to C:\IOLOG. This file is rewritten when the TSR goes
  9.  resident. If you use a different file, be sure to specify a complete path
  10.  name, since IOLOG opens and closes this file for each I/O operation. If you
  11.  haven't specified a complete path name and you change directories, you'll get
  12.  little log files scattered all over the place.
  13.  
  14.  The log file is a text file showing information about each interesting DOS
  15.  I/O call. It lists the call and then the associated file handle or name, as
  16.  available. If DOS detected an error during the call, column 9 of the report
  17.  shows an asterisk, unless reporting of failed calls is disabled. IOLOG does
  18.  not log its own I/O operations.
  19.  
  20.  IOLOG uses a buffer to hold log information until it is safe to write it to
  21.  disk. In the unexpected event that the buffer overflows before it can be
  22.  emptied, the log file will contain one or more lines showing just an
  23.  exclamation point.
  24.  
  25.  Certain aspects of IOLOG can be controlled by pressing hot keys after it has
  26.  gone resident. The following table summarizes the hot keys:
  27.  
  28.    <LeftShift><RightShift><L>     toggles logging            (default ON)
  29.    <LeftShift><RightShift><W>     toggles read/write logging (default OFF)
  30.    <LeftShift><RightShift><F>     toggles failure logging    (default ON)
  31.    <LeftShift><RightShift><C>     clears log file
  32.    <LeftShift><RightShift><U>     unloads IOLOG from memory
  33.  
  34.  When logging is off, no further I/O operations will be logged until it is
  35.  turned on again. Then any new operations will be appended to the existing log
  36.  file. The Read/Write toggle controls whether DOS functions $3F and $40 are
  37.  logged. In some cases, these calls are made quite often, and logging them
  38.  will both decimate the performance of any program that is running and also
  39.  create huge log files. The Failure toggle controls whether DOS calls with
  40.  errors are reported, e.g., a request to open a file that isn't found. If
  41.  failures are logged, they are denoted by an asterisk in column 9 of the log.
  42.  Otherwise, failing calls do not appear at all.
  43.  
  44.  IOLOG uses sound effects to let you know whether you're toggling a feature on
  45.  or off. When you toggle something ON, a rising sequence of tones sounds. OFF,
  46.  and a falling sequence is used. When you clear the log file, you get a muddy
  47.  sounding warble. And "taps" plays when you successfully unload IOLOG from
  48.  memory.
  49.  
  50.  IOLOG uses about 19K of memory while installed.
  51.  
  52.  Requires Turbo Professional to compile.
  53.  Released to the public domain.
  54.  
  55.  Written by Kim Kokkonen, TurboPower Software
  56.  Thanks to Don Pearsall for providing the impetus to write this program.
  57.  
  58.  Version 1.0 - 12/16/88
  59.    initial release
  60. }
  61.  
  62. {$R-,V-,S-,B-,I-}                 {Turn off all the checking}
  63. {$M 1024,100,100}                 {Small stack, no heap}
  64.  
  65. {WARNING: this program has been carefully optimized to use small stacks
  66.  like the 1024 byte main program stack above. Do not modify this program or
  67.  use it as a model for other TSRs without checking out the stack usage first}
  68.  
  69. program IOLog;
  70.  
  71. uses
  72.   Dos, TpInt, TpTsr;              {TPINT and TPTSR are from Turbo Professional}
  73.  
  74. const
  75.   TimerHandle = 15;               {ISR handle for int 1C}
  76.   DosHandle = 16;                 {ISR handle for int 21}
  77.   LogBufSize = 1024;              {Bytes in log buffer}
  78.   ToggleLogKey = $0326;           {Toggle logging       - <LShift><RShift><L>}
  79.   ClearLogKey = $032E;            {Clear log file       - <LShift><RShift><C>}
  80.   RWLogKey = $0311;               {Log read/write calls - <LShift><RShift><W>}
  81.   FailLogKey = $0321;             {Toggle fail reports  - <LShift><RShift><F>}
  82.   UnloadKey = $0316;              {Unload logger        - <LShift><RShift><U>}
  83.  
  84. var
  85.   LogFileName : string[63];       {Name chosen for log file}
  86.   LogFile : file;                 {Untyped log file}
  87.   LogBuffer : array[1..LogBufSize] of Char; {Temporarily stores log info}
  88.   LogBufPos : Word;               {Next available position in log buffer}
  89.   LogString : string[99];         {Temporary string to build log transaction}
  90.  
  91.   LogChangeHandle : Byte;         {SetPopTicker handler for log to disk routine}
  92.   DosStack : array[1..1024] of Byte; {Stack used by int 21 handler}
  93.   HotStack : array[1..512] of Byte; {Stack used by hotkey popups}
  94.  
  95.   AccessingLogFile : Boolean;     {True while logging to disk}
  96.   BufferingLogInfo : Boolean;     {True while int 21 handler active}
  97.   LoggingIsActive : Boolean;      {True while we're gathering log info}
  98.   LoggingReadWrite : Boolean;     {True to log read/write calls}
  99.   ShowFailures : Boolean;         {True to show failing IO calls}
  100.  
  101.   CountsPerMs : Word;             {Tight loop count for one millisecond}
  102.  
  103.  
  104.  
  105.   procedure Delay(Ms : Word);
  106.     {-Delay for specified number of milliseconds}
  107.   var
  108.     DummyLow : Word absolute $0 : $0;
  109.     InitTicks : Word;
  110.     Counts : Word;
  111.     Done : Boolean;
  112.     T : Word;
  113.   begin
  114.     for T := 1 to Ms do begin
  115.       InitTicks := DummyLow;
  116.       Counts := 0;
  117.       repeat
  118.         Inc(Counts);
  119.         Done := (Counts = CountsPerMs) or (DummyLow <> InitTicks);
  120.       until Done;
  121.     end;
  122.   end;
  123.  
  124.   procedure CalibrateDelay;
  125.     {-Delay calibration routine, similar to CRT unit}
  126.   var
  127.     BiosTickLow : Word absolute $40 : $6C; {BIOS timer data}
  128.     InitTicks : Word;
  129.     Counts : Word;
  130.     Done : Boolean;
  131.   begin
  132.     InterruptsOn;
  133.     CountsPerMs := 50000;          {Upper limit for counts per tick}
  134.  
  135.     {Wait for tick to change}
  136.     InitTicks := BiosTickLow;
  137.     repeat
  138.     until BiosTickLow <> InitTicks;
  139.  
  140.     {Now count until it changes again or it reaches a limit}
  141.     InitTicks := BiosTickLow;
  142.     Counts := 0;
  143.     repeat
  144.       Inc(Counts);
  145.       Done := (Counts = CountsPerMs) or (BiosTickLow <> InitTicks);
  146.     until Done;
  147.  
  148.     {Convert to counts per millisecond}
  149.     CountsPerMs := Counts div 55;
  150.   end;
  151.  
  152.   procedure Sound(Freq : Word);
  153.     {-Activate speaker at specified frequency}
  154.   var
  155.     Count : Word;
  156.     SoundPort : Byte;
  157.   begin
  158.     Count := $1234DC div Freq;
  159.     SoundPort := Port[$61];
  160.     Port[$61] := SoundPort or 3;  {Program sound port}
  161.     Port[$43] := $B6;             {Tell timer a count is coming}
  162.     Port[$42] := Lo(Count);
  163.     Port[$42] := Hi(Count);
  164.   end;
  165.  
  166.   procedure NoSound;
  167.     {-Deactivate sound}
  168.   var
  169.     SoundPort : Byte;
  170.   begin
  171.     SoundPort := Port[$61];
  172.     Port[$61] := SoundPort and $FC;
  173.   end;
  174.  
  175.   procedure Note(Freq, Dura : Word);
  176.     {-Sound a note}
  177.   begin
  178.     Sound(Freq);
  179.     Delay(Dura);
  180.     NoSound;
  181.   end;
  182.  
  183.   procedure UpSound;
  184.     {-Rising tones}
  185.   var
  186.     F : Word;
  187.   begin
  188.     for F := 2 to 12 do
  189.       Note(F*200, 50);
  190.   end;
  191.  
  192.   procedure DownSound;
  193.     {-Falling tones}
  194.   var
  195.     F : Word;
  196.   begin
  197.     for F := 12 downto 2 do
  198.       Note(F*200, 50);
  199.   end;
  200.  
  201.   procedure MuddySound;
  202.     {-Sound made when deleting file}
  203.   var
  204.     F : Word;
  205.     G : Word;
  206.   begin
  207.     for F := 1 to 5 do begin
  208.       Sound(100);
  209.       Delay(100);
  210.       for G := 10 to 20 do begin
  211.         Sound(G*100);
  212.         Delay(10);
  213.       end;
  214.     end;
  215.     NoSound;
  216.   end;
  217.  
  218.   procedure SaluteSound;
  219.     {-Sound made when unloading TSR}
  220.   begin
  221.     Note(400, 150);
  222.     Delay(50);
  223.     Note(400, 50);
  224.     Delay(50);
  225.     Note(550, 700);
  226.     Delay(50);
  227.  
  228.     Note(400, 150);
  229.     Delay(50);
  230.     Note(550, 50);
  231.     Delay(50);
  232.     Note(700, 700);
  233.   end;
  234.  
  235.   procedure StUpcaseVar(var S : string);
  236.     {-Raise string to uppercase}
  237.   var
  238.     I : Integer;
  239.   begin
  240.     for I := 1 to Length(S) do
  241.       S[I] := UpCase(S[I]);
  242.   end;
  243.  
  244.   procedure GetFileName(var Regs : IntRegisters);
  245.     {-Convert ASCIIZ string at DS:DX to a Turbo string}
  246.   type
  247.     Carray = array[0..100] of Char;
  248.   var
  249.     Cptr : ^Carray;
  250.     Len : Word;
  251.   begin
  252.     Cptr := Ptr(Regs.Ds, Regs.Dx);
  253.     Len := 0;
  254.     while (Len < 79) and (Cptr^[Len] <> #0) do begin
  255.       Inc(Len);
  256.       LogString[Len] := UpCase(Cptr^[Len-1]);
  257.     end;
  258.     LogString[0] := Char(Len);
  259.   end;
  260.  
  261.   procedure LogToBuffer;
  262.     {-Add the latest LogString to the end of the buffer, or show overflow}
  263.   const
  264.     CrLf : string[2] = ^M^J;      {Terminates each line in log file}
  265.     Overflow = '!';               {String written to show buffer overflow}
  266.   begin
  267.     AccessingLogFile := True;
  268.     if LogBufPos+Length(LogString)+3 <= LogBufSize then begin
  269.       {Fits in buffer, with room for an overflow marker still}
  270.       Move(LogString[1], LogBuffer[LogBufPos], Length(LogString));
  271.       Inc(LogBufPos, Length(LogString));
  272.       Move(CrLf[1], LogBuffer[LogBufPos], 2);
  273.       Inc(LogBufPos, 2);
  274.     end else if LogBufPos+2 <= LogBufSize then begin
  275.       {Overflow marker fits in buffer}
  276.       LogBuffer[LogBufPos] := Overflow;
  277.       Inc(LogBufPos);
  278.       Move(CrLf[1], LogBuffer[LogBufPos], 2);
  279.       Inc(LogBufPos, 2);
  280.     end;
  281.     AccessingLogFile := False;
  282.   end;
  283.  
  284.   procedure ToggleFlag(var Flag : Boolean);
  285.     {-Toggle any log flag}
  286.   begin
  287.     PopupsOff;
  288.     Flag := not Flag;
  289.     {Make a sound}
  290.     if Flag then
  291.       UpSound
  292.     else
  293.       DownSound;
  294.     PopupsOn;
  295.   end;
  296.  
  297.   {$F+}
  298.   procedure WriteLogFile(var Regs : Registers);
  299.     {-Called via SetPopTicker to update log file}
  300.   var
  301.     ErrorCode : Word;
  302.   begin
  303.     AccessingLogFile := True;
  304.     Assign(LogFile, LogFileName);
  305.     Reset(LogFile, 1);
  306.     if IoResult = 0 then
  307.       Seek(LogFile, FileSize(LogFile))
  308.     else
  309.       Rewrite(LogFile, 1);
  310.     if IoResult = 0 then begin
  311.       BlockWrite(LogFile, LogBuffer, LogBufPos-1);
  312.       Close(LogFile);
  313.       ErrorCode := IoResult;
  314.     end;
  315.     LogBufPos := 1;
  316.     AccessingLogFile := False;
  317.   end;
  318.  
  319.   procedure ToggleLogging(var Regs : Registers);
  320.     {-Toggle logging - activated by <LShift><RShift><L>}
  321.   begin
  322.     ToggleFlag(LoggingIsActive);
  323.   end;
  324.  
  325.   procedure ToggleRW(var Regs : Registers);
  326.     {-Toggle logging of read/write calls - activated by <LShift><RShift><W>}
  327.   begin
  328.     ToggleFlag(LoggingReadWrite);
  329.   end;
  330.  
  331.   procedure ToggleFail(var Regs : Registers);
  332.     {-Toggle logging of failed calls - activated by <LShift><RShift><F>}
  333.   begin
  334.     ToggleFlag(ShowFailures);
  335.   end;
  336.  
  337.   procedure ClearLog(var Regs : Registers);
  338.     {-Clear the current log file - activated by <LShift><RShift><C>}
  339.   var
  340.     ErrorCode : Word;
  341.   begin
  342.     PopupsOff;
  343.     AccessingLogFile := True;
  344.     Assign(LogFile, LogFileName);
  345.     Rewrite(LogFile, 1);
  346.     if IoResult = 0 then begin
  347.       Close(LogFile);
  348.       ErrorCode := IoResult;
  349.       {Make a sound}
  350.       MuddySound;
  351.     end;
  352.     LogBufPos := 1;
  353.     AccessingLogFile := False;
  354.     PopupsOn;
  355.   end;
  356.  
  357.   procedure Unload(var Regs : Registers);
  358.     {-Unload from memory - activated by <LShift><RShift><U>}
  359.   begin
  360.     if DisableTSR then
  361.       SaluteSound;
  362.   end;
  363.  
  364.   procedure TimerMonitor(BP : Word); interrupt;
  365.     {-ISR called on every int 1C}
  366.   var
  367.     Regs : IntRegisters absolute BP;
  368.   begin
  369.     EmulateInt(Regs, ISR_Array[TimerHandle].OrigAddr);
  370.     if (LogBufPos > 1) and not AccessingLogFile then
  371.       SetPopTicker(LogChangeHandle, 18);
  372.   end;
  373.  
  374.   procedure DosHandler(var Regs : IntRegisters);
  375.     {-Called during int 21 to buffer log strings}
  376.   const
  377.     OpenName : array[$3C..$3D] of string[10] = ('create    ', 'open      ');
  378.     DupName : array[$45..$46] of string[10] = ('dup       ', 'cdup      ');
  379.     ChModName : array[0..1] of string[13] = ('get mode     ', 'set mode     ');
  380.     DateName : array[0..1] of string[10] = ('get date  ', 'set date  ');
  381.     CloseName : string[10] = 'close     ';
  382.     DeleteName : string[13] = 'delete       ';
  383.     FindName : string[13] = 'findfile     ';
  384.     RenameName : string[13] = 'rename       ';
  385.     ReadName : string[10] = 'read      ';
  386.     WriteName : string[10] = 'write     ';
  387.   var
  388.     CallAH : Byte;
  389.     CallAL : Byte;
  390.     HandleString : string[5];
  391.   begin
  392.     with Regs do begin
  393.       {Save a few items we may need later}
  394.       CallAH := AH;
  395.       CallAL := AL;
  396.       Str(BX:2, HandleString);
  397.  
  398.       {Save the filename when available}
  399.       case CallAH of
  400.         $3C, $3D, $41, $43, $4E, $56 : GetFileName(Regs);
  401.       end;
  402.  
  403.       {Call DOS to do the real work}
  404.       EmulateInt(Regs, ISR_Array[DosHandle].OrigAddr);
  405.  
  406.       {Save the rest of the info}
  407.       case CallAH of
  408.         $3C..$3D :                {Opening or creating a file}
  409.           begin
  410.             if Odd(Flags) then
  411.               HandleString := '  '
  412.             else
  413.               Str(AX:2, HandleString);
  414.             LogString := ' '+LogString;
  415.             LogString := HandleString+LogString;
  416.             LogString := OpenName[CallAH]+LogString;
  417.           end;
  418.         $3E :                     {Closing a file}
  419.           LogString := CloseName+HandleString;
  420.         $3F :                     {Read file}
  421.           LogString := ReadName+HandleString;
  422.         $40 :                     {Write file}
  423.           LogString := WriteName+HandleString;
  424.         $41 :                     {Erasing a file}
  425.           LogString := DeleteName+LogString;
  426.         $43 :                     {Get or set attribute}
  427.           LogString := ChModName[CallAL]+LogString;
  428.         $45, $46 :                {Dup handle}
  429.           LogString := DupName[CallAH]+HandleString;
  430.         $4E :                     {Find first}
  431.           LogString := FindName+LogString;
  432.         $56 :                     {Rename}
  433.           LogString := RenameName+LogString;
  434.         $57 :                     {Date/time}
  435.           LogString := DateName[CallAL]+HandleString;
  436.       else
  437.         Exit;                     {Shouldn't get here, protect just in case}
  438.       end;
  439.  
  440.       if Odd(Flags) then
  441.         if ShowFailures then
  442.           {Indicate an error in the call}
  443.           LogString[9] := '*'
  444.         else
  445.           Exit;
  446.  
  447.       {Store the string in the buffer}
  448.       LogToBuffer;
  449.     end;
  450.   end;
  451.  
  452.   procedure DosMonitor(BP : Word); interrupt;
  453.     {-ISR called on every int 21}
  454.   var
  455.     Regs : IntRegisters absolute BP;
  456.     LogThisCall : Boolean;
  457.   begin
  458.     if not LoggingIsActive or AccessingLogFile or BufferingLogInfo then
  459.       {Don't log our own log operations or overwrite swap stack}
  460.       LogThisCall := False
  461.     else
  462.       case Regs.AH of
  463.         $3F, $40 : LogThisCall := LoggingReadWrite;
  464.         $3C..$3E, $41, $43, $45..$46, $4E, $56..$57 : LogThisCall := True;
  465.       else
  466.         LogThisCall := False;
  467.       end;
  468.  
  469.     if LogThisCall then begin
  470.       BufferingLogInfo := True;
  471.       SwapStackAndCall(@DosHandler, @DosStack[SizeOf(DosStack)], Regs);
  472.       BufferingLogInfo := False;
  473.     end else
  474.       ChainInt(Regs, ISR_Array[DosHandle].OrigAddr)
  475.   end;
  476.   {$F-}
  477.  
  478.   procedure Abort(Msg : string);
  479.     {-Write a message and halt}
  480.   begin
  481.     WriteLn(Msg);
  482.     Halt(1);
  483.   end;
  484.  
  485. begin
  486.   {Create the log file}
  487.   if ParamCount > 0 then
  488.     LogFileName := ParamStr(1)
  489.   else
  490.     LogFileName := 'C:\IOLOG';
  491.   StUpcaseVar(LogFileName);
  492.   Assign(LogFile, LogFileName);
  493.   Rewrite(LogFile, 1);
  494.   if IoResult <> 0 then begin
  495.     WriteLn('Could not create ', LogFileName);
  496.     Abort('Usage: IOLOG [LogFileName]');
  497.   end;
  498.   Close(LogFile);
  499.  
  500.   {Initialize global variables}
  501.   CalibrateDelay;
  502.   LogBufPos := 1;
  503.   AccessingLogFile := False;
  504.   BufferingLogInfo := False;
  505.  
  506.   {These flags control IOLOG's behavior}
  507.   LoggingIsActive := True;
  508.   LoggingReadWrite := False;
  509.   ShowFailures := True;
  510.  
  511.   {Grab interrupt vectors and set up hot keys}
  512.   if
  513.   not InitVector($1C, TimerHandle, @TimerMonitor) or
  514.   not InitVector($21, DosHandle, @DosMonitor) or
  515.   not DefinePopProc(LogChangeHandle, @WriteLogFile, Ptr(SSeg, SPtr)) or
  516.   not DefinePop(ToggleLogKey, @ToggleLogging, @HotStack[SizeOf(HotStack)], True) or
  517.   not DefinePop(ClearLogKey, @ClearLog, @HotStack[SizeOf(HotStack)], True) or
  518.   not DefinePop(RWLogKey, @ToggleRW, @HotStack[SizeOf(HotStack)], True) or
  519.   not DefinePop(FailLogKey, @ToggleFail, @HotStack[SizeOf(HotStack)], True) or
  520.   not DefinePop(UnloadKey, @Unload, @HotStack[SizeOf(HotStack)], True)
  521.   then
  522.     Abort('Unable to install IOLOG');
  523.  
  524.   WriteLn('IOLOG 1.0  -- by TurboPower Software -- DOS File Logging');
  525.   WriteLn('  <LeftShift><RightShift><L> toggles logging            (default ON)');
  526.   WriteLn('  <LeftShift><RightShift><W> toggles read/write logging (default OFF)');
  527.   WriteLn('  <LeftShift><RightShift><F> toggles failure logging    (default ON)');
  528.   WriteLn('  <LeftShift><RightShift><C> clears log file');
  529.   WriteLn('  <LeftShift><RightShift><U> unloads IOLOG from memory');
  530.   WriteLn;
  531.   WriteLn('Logging results to ', LogFileName);
  532.  
  533.   {Activate popups and go resident}
  534.   PopupsOn;
  535.   if not TerminateAndStayResident(ParagraphsToKeep, 0) then
  536.     Abort('Unable to go resident');
  537. end.
  538.